#include <UnitTest++.h>
#include "df.hpp"
#include <algorithm>

TEST(affichage_df) {
  cout << endl << "----TEST(affichage_df)----" << endl;
  df_t f, g;
    cout << f << endl;
  f._src.insert("C");
    cout << f << endl;
  data_t d = {"D", "A"};
  f._dst = d;
    cout << f << endl;
  g._src = {"B", "B"};
  g._dst = {"A", "C"};
    cout << g << endl;

/***** doit afficher ****
 -> 
C -> 
C -> A,D
B -> A,C
*************************/
}

TEST(affichage_dfs) {
  cout << endl << "----TEST(affichage_dfs)----" << endl;
  df_t f({"A"}, {"B"});
  df_t g({"A"}, {"C"});
  dfs_t s = {g,f};
    cout << s;
/***** doit afficher ****
A -> C
A -> B
*************************/
}

TEST(comparaison_df) {
  cout << endl << "----TEST(comparaison_df)----" << endl;
  df_t f({"A"}, {"B"});
  df_t g({"A"}, {"C"});
  CHECK(f == f);
  CHECK(!(f == g));
  CHECK(f <  g);
  CHECK(!(g <  f));
}

TEST(exemple){
  cout << endl << "----TEST(exemple)----"<< endl;
  df_t f({"A"},{"B","C"});
  //crée une DF A -> B,C
  cout << f << endl;
/***** doit afficher ****
A -> B,C
*************************/
  dfs_t F({df_t({"B"},{"A"}),df_t({"A"},{"B","C"})});
  cout << F << endl;
/***** doit afficher ****
B -> A
A -> B,C
*************************/  
  CHECK(f == F[1]);
  CHECK(count(F.cbegin(),F.cend(),f)==1);
  //voir http://www.cplusplus.com/reference/algorithm/count/
}


TEST(closurenaif) {
  cout << endl << "---- TEST (Cloture naive) ----" << endl; 
  
  data_t chk;
  data_t r;
  data_t res;

  dfs_t F({df_t({"B"},{"A"}),df_t({"A"},{"B","C"}), df_t({"D"}, {"A", "C"})});
  cout << F << endl;

  r = schema(F);
 
  chk.insert("D");
/*  chk.insert("B");
  chk.insert("C");
  chk.insert("D");
*/
  cout << "Fermeture de " << chk << " : " << endl;
  res = closure(F, chk);
  cout << res << endl;

  CHECK(res == r);
}


TEST(schema) {
  cout << endl << "---- TEST (Schema) ----" << endl; 
  
  data_t r;
  data_t chk;

  cout << "  -- 0 attributs --" << endl;
  df_t f;
  dfs_t F1;
  F1.push_back(f);
  cout << F1 << endl;

  r = schema(F1);
  cout << r << endl;
  CHECK(r == chk);

  cout << "  -- 4 attributs --" << endl;
  dfs_t F2({df_t({"B"},{"A"}),df_t({"A"},{"B","C"}), df_t({"D"}, {"A", "C"})});
  cout << F2 << endl;
 
  chk.insert("A");
  chk.insert("B");
  chk.insert("C");
  chk.insert("D");


  r = schema(F2);
  cout << r << endl;
  CHECK(r == chk);
}

TEST(models) {
  cout << endl <<  "---- TEST (Models) ----" << endl; 
  
  bool res;

  dfs_t F({df_t({"B"},{"A"}),df_t({"A"},{"B","C"}), df_t({"D"}, {"A", "C"})});
  cout << F << endl;
 
  df_t x({"D"}, {"B"});
  cout << x << endl;

  res = models(F, x);

  CHECK(res == true);
}

TEST(key) {

  cout << endl << "---- Test key() ----" << endl;
  dfs_t F({df_t({"B"},{"A"}),df_t({"A"},{"B","C"}), df_t({"D"}, {"A", "C"})});
  cout << F << endl;
  
  data_t schema_F = schema(F);

  cout << "Attributs de F : " << schema_F << endl;

  data_t d;
  d.insert("D");
  data_t closure_Fd = closure(F, d);
  cout << "Fermeture de D selon F : " << closure_Fd << endl;

  data_t c;
  c.insert("C");
  data_t closure_Fc = closure(F, c);
  cout << "Fermeture de C selon F : " << closure_Fc << endl << endl;

  bool key_d = key(F, d); 
  bool key_c = key(F, c);

  CHECK(key_d);
  CHECK(!key_c);

  CHECK(1 == 1);

}

TEST(buildcount) {
  
  cout << endl << "---- Test buildcount() ----" << endl;
  dfs_t F({df_t({"B"},{"A"}),df_t({"A"},{"B","C"}), df_t({"D", "B"}, {"A", "C"})});
  cout << F << endl;
    
  count_t index_F = buildcount(F);
  count_t verif;

  for(auto it = index_F.begin(); it != index_F.end(); it++)
    cout << *it << endl;
  
  verif.push_back((size_t)1);
  verif.push_back((size_t)1);
  verif.push_back((size_t)2);

  CHECK(
      includes(verif.begin(), verif.end(), index_F.begin(), index_F.end()) &&
      includes(index_F.begin(), index_F.end(), verif.begin(), verif.end())
      );
}

TEST(buildindex){
  cout << endl << "---- Test buildindex() ----" << endl;

  dfs_t F({df_t({"G"},{"E","B","D"}),
      df_t({"E"},{"D"}),
      df_t({"D"},{"C","B","A"}),
      df_t({"A","E"},{"B"})});
    
  cout << F << endl;

  index_t testBuild = buildindex(F); 
  
  cout<< " Affichage de la map " <<endl; 
    

  for(auto it_m = testBuild.begin(); it_m!=testBuild.end(); it_m++) {
    /* On affiche le premier element */
    cout << it_m->first << " : ";
    /* On affiche la liste maintenant (deuxieme element) */
    for (auto it_l = it_m->second.begin(); it_l != it_m->second.end(); it_l++) {
      cout << " " << *it_l;
    }
    cout << endl;
  }
                              
}

TEST(closurelin) {
  
  cout << endl << "---- Test closurelin() ----" << endl;

  dfs_t F({df_t({"G"},{"E","B","D"}),
      df_t({"E"},{"D"}),
      df_t({"D"},{"C","B","A"}),
      df_t({"A","E"},{"B"})});
  cout << F << endl;

  index_t idx = buildindex(F);
  
  data_t test;
  test.insert("G");
  
  data_t res = closurelin(F, test, idx);
  
  cout << "Fermture de G : " << endl;
  cout << res << endl;

  data_t verif = closure(F, test);

  CHECK(includes(res.begin(), res.end(), verif.begin(), verif.end()) && includes(verif.begin(), verif.end(), res.begin(), res.end()));
}

TEST(generate) {
  cout << endl << "---- Test generate() ----" << endl;
  
  size_t n;
  n = 10;
  
  dfs_t test = generate(n);
  cout << "Generation d'un ensemble de " << n << " DFs : " << endl;
  cout << test << endl;

  /* cout << "taille : " << test.size() << endl; */

  CHECK(n == test.size());

  cout << "Generation d'un ensemble de DFs vide" << endl;

  dfs_t test2 = generate(0);

  CHECK(0 == test2.size());
}

/*********** main ***********/

int main()
{
    return UnitTest::RunAllTests(); // un peu magique: lance tous les tests
}
